package de.graph.common.impl;

import java.util.Collection;

import de.graph.common.Edge;
import de.graph.common.Graph;
import de.graph.common.Vertex;
import de.graph.common.impl.GraphFactory.GraphType;
import de.graph.document.Document;
import de.graph.document.DocumentFassade;

final class GraphImpl extends DocumentFassade implements Graph {

    private static final long serialVersionUID = 2513670791704732220L;

    protected GraphImpl(final Document document) {
        super(document);
    }

    @Override
    public Vertex createVertex() {
        Vertex vertex = VertexFactory.create();

        getVertexSet().add(vertex);

        return vertex;
    }

    @Override
    public Vertex createVertex(String label) {

        Vertex vertex = VertexFactory.create(label);

        getVertexSet().add(vertex);

        return vertex;
    }

    @Override
    public void removeVertex(Vertex vertex) {
        for (Edge outEdge : vertex.getOutEdges()) {
            Vertex target = outEdge.getTargetVertex();

            target.getInEdges().remove(outEdge);

            if (!isDirected()) {
                target.getOutEdges().remove(outEdge);
            }

            getEdgeSet().remove(outEdge);
        }

        for (Edge inEdge : vertex.getInEdges()) {
            Vertex source = inEdge.getSourceVertex();

            source.getOutEdges().remove(inEdge);

            if (!isDirected()) {
                source.getInEdges().remove(inEdge);
            }

            getEdgeSet().remove(inEdge);
        }

        getVertexSet().remove(vertex);
    }

    @Override
    public Collection<Vertex> getVertices() {
        return getVertexSet();
    }

    @Override
    public Collection<Vertex> getVertices(String label) {
        // TODO
        return getVertexSet();
    }

    @Override
    public Edge createEdge(Vertex source, Vertex target) {
        return createEdge(source, target, null);
    }

    @Override
    public Edge createEdge(Vertex source, Vertex target, String label) {
        if (source == null) {
            throw new IllegalArgumentException("Parameter 'source' must not be null!");
        }

        if (target == null) {
            throw new IllegalArgumentException("Parameter 'target' must not be null!");
        }

        Edge edge = EdgeFactory.create(source, target, isDirected(), label);

        source.getOutEdges().add(edge);
        target.getInEdges().add(edge);

        if (!isDirected()) {
            target.getOutEdges().add(edge);
            source.getInEdges().add(edge);
        }

        getEdgeSet().add(edge);

        return edge;
    }

    @Override
    public void removeEdge(Edge edge) {
        edge.getSourceVertex().getOutEdges().remove(edge);
        edge.getTargetVertex().getInEdges().remove(edge);

        if (!isDirected()) {
            edge.getTargetVertex().getOutEdges().remove(edge);
            edge.getSourceVertex().getInEdges().remove(edge);
        }

        getEdgeSet().remove(edge);
    }

    @Override
    public Collection<Edge> getEdges() {
        return getEdgeSet();
    }

    @Override
    public Collection<Edge> getEdges(String label) {
        // TODO
        return getEdgeSet();
    }

    @Override
    public boolean isDirected() {
        return (getProperty(GraphFactory.GRAPH_TYPE) == GraphType.DIRECTED);
    }

    @Override
    public void clear() {
        for (Vertex vertex : getVertices()) {
            ((ElementImpl) vertex).clear();
        }
        getVertexSet().clear();

        for (Edge edge : getEdges()) {
            ((ElementImpl) edge).clear();
        }
        getEdgeSet().clear();

        super.clear();
    }

    private Collection<Vertex> getVertexSet() {
        return getProperty(GraphFactory.VERTEX_SET);
    }

    private Collection<Edge> getEdgeSet() {
        return getProperty(GraphFactory.EDGE_SET);
    }
}
